include(['HexGrid', 'PClass', 'exceptions.Assert', 'com.unknown.Constants'],

function() {
	/*
	 * Idea - Add another class for EndlessGrid called EndlessGridCellFactory
	 *  which should allow for different types of grids (hex, square, pixel) 
	 */
	
	declare('EndlessGrid');

	
	
	EndlessGrid = PClass.create({
		container       : {},
		cellLoader      : function(){},
		visible         : {rows:0, columns:0},
		total           : {rows:0, columns:0},
		gridMeta        : {
			cellHeight : 0,
			cellWidth  : 0,
			rowCount   : 0,
			colCount   : 0,
			height     : 0,
			width      : 0,
			recalculate : function(){
				this.height = (this.cellHeight * this.rowCount );
				this.width  = (this.cellWidth  * this.colCount );
			}
		},
		cells      : [],
		cellGrid   : [],
		m2d        : new Matrix2d(),
		_lastHit   : null,
		_lastGrid  : null,
		renderGrids : false,
		/**
		 * EndlessGrid.ctor(): 
		 * 	 function(container, GridMeta)
		 *   function(container, cellWidth, cellHeight, rowCount, colCount)
		 * 
		 */
		init : function(container, cellLoader, cellWidth, cellHeight, rowCount, colCount){
			Assert.require({container:container});
			Assert.hasField(container, ['width', 'height']);
			
			var gm = this.gridMeta;
			var safeSet = this.__safeSet;
			this.container = container;
			this.cellLoader = cellLoader || this.cellLoader;
			
			if( arguments.length > 3 ){
				safeSet(gm, 'cellHeight', cellHeight);
				safeSet(gm, 'cellWidth', cellWidth);
				safeSet(gm, 'rowCount', rowCount);
				safeSet(gm, 'colCount', colCount);
			}
			else if(arguments.length == 3){
				var data = arguments[2];

				safeSet(gm, 'cellHeight', data.cellHeight);
				safeSet(gm, 'cellWidth', data.cellWidth);
				safeSet(gm, 'rowCount', data.rowCount);
				safeSet(gm, 'colCount', data.colCount);
			}
			if( gm.rowCount == 0 || gm.colCount == 0 )
				throw 'RowCount, Column Count required';
			
			this.resize();
		},
		resize : function(){
			var width  = this.container.width;
			var height = this.container.height;
			
			this.gridMeta.recalculate();
			
			this.visible.rows = Math.ceil(height / this.gridMeta.height);
			this.visible.columns = Math.ceil(width / this.gridMeta.width);
			
			this.total.rows = this.visible.rows+1;
			this.total.columns = this.visible.columns+1;
			
			this._rebuild();
		},
		_debugCell : function(x,y){
			var cell = { 
				grid : { 
					getCell : function(){}
					,translate : function(x,y){
						this.node.matrix2d.translate(x,y);
					}
					,node : { 
						matrix2d : new Matrix2d()
					}
					, render : function(){}
					, hit : function(){}
					, select : function(){}
					, activeCells : []
				}
				,getCell : function(){}
			} 
			cell.grid.getCell = function(){
				return {
					owner : cell.grid
					,select : function(){}
					,x : 100
					,y : 100
					,contents : 'test'
				}
			}
			cell.grid.node.matrix2d.x3 = x;
			cell.grid.node.matrix2d.y3 = y;
		},
		getNeighbor : function(grid, dir){
			var rows = this.total.rows;
			var cols = this.total.columns;
			var col = 0, row = 0;
			var debug = "Grid[" + grid.col + ", " + grid.row +"] ";
			if((Direction.LEFT & dir) == Direction.LEFT){
				col = -1;
				debug += "Left";
			}
			if((Direction.RIGHT & dir) == Direction.RIGHT){
				col = 1;
				debug += "Right";
			}
			if((Direction.UP & dir) == Direction.UP){
				row = -1;
				debug += ", Up";
			}
			if((Direction.DOWN & dir) == Direction.DOWN){
				row = 1;
				debug += ", Down";
			}
			col = ((cols+grid.col+col+1)%cols);
			row = ((rows+grid.row+row+1)%rows);
			var result = this.cellGrid[row][col].grid;
			debug += " is at " + result.col + ", " + result.row + " ("+col+", "+row+")";
			//console.log(debug);
			return result;
		},
		_rebuild : function(){
			var rows = this.total.rows;
			var cols = this.total.columns;
			
			var width = this.gridMeta.width;
			var height = this.gridMeta.height;
			
			for(var row =-1; row < rows-1; row++){
				var cellrow = []
				for(var col=-1; col < cols-1; col++){
					var x = col * width;
					var y = row * height;
					
					var cell = this._buildHex(x,y);
					cell.grid.left = x;
					cell.grid.top = y;
					cell.grid.roomId = room.id;
					cell.grid.owner = this;
					cell.grid.row = row;
					cell.grid.col = col;
					this.cellLoader(cell.grid, this.gridMeta, {row:row, col:col}, x, y);
					this.cells.push(cell);
					cellrow.push( cell );
				}
				this.cellGrid.push( cellrow );
			}
			var cg = this.cellGrid;
			iterate(cg, function(array, row){
				iterate(array, function(item, col){
					try{
						col = ((col+1)%cols);
						row = ((row+1)%rows);
						
						//console.log("Neighbor of " + key + " is " + col + "," + row );
						var neighbor = cg[row][col]
						var grid = item.grid;
						var grid2 = neighbor.grid;
						grid.previous = grid2;
						
						col = ((cols+col-1)%cols);
						row = ((rows+row-1)%rows);
						neighbor = cg[row][col]
						grid2 = neighbor.grid;
						grid.next = grid2;
					}catch(e){
						console.error(e);
					}
				});
				
			});
		},	
		reload : function(){
			var callback = this.cellLoader;
			iterate(this.cells, function(cell){
				callback(cell.grid);
			});
			this.cells = [];
			this._rebuild();
			
			var m2d = this.m2d;
			m2d.changed = true;
			m2d.setTranslation(0,0);
		},		
		_buildHex : function(x, y){
			var columns = this.gridMeta.colCount;
			var rows = this.gridMeta.rowCount;
			
			var hex1 = new HexGrid(0,0,columns,rows);
			hex1.translate(x, y);
			
			var n1 = new Node();
			n1.addChild(hex1);
			return {node:n1, grid:hex1}
		},
		render : function(gw){
			var c = gw.c;
			var cells = this.cells;
			var m2d = this.m2d;			
			var renderChildren = function(gw, renderer){
				var c = gw.c;
				iterate(cells, function(cell){
					try{
						c.save();
						renderer.renderObject(gw, cell.grid);
					}finally{
						c.restore();
					}
				});
			};
			if( this.renderGrids )
				iterate(gw.renderers, function(renderer, index){
					renderChildren(gw, renderer);
				});
			if( m2d.changed ){
				floor.style['-webkit-transform']= 'translate('+m2d.x3+'px, ' + m2d.y3+'px)'
				m2d.changed = false;
			}
		},		
		hit : function(e){
			iterate(this.cells, function(cell){
				cell.grid.hit(e);
			});
		},
		getCell : function(x,y){
			var result = null;
			if( this._lastHit && this._lastHit.owner.checkHit(this._lastHit, x,y) ){
				return this._lastHit;
			}			
			if( this._lastGrid ){
				result = this._lastGrid.getCell(x,y);
			}
			if(!result){
				iterate(this.cells, function(cell){
					result = cell.grid.getCell(x,y);
					return result;
				});
			}
			if( result ){
                this._lastGrid = result.owner;
				this._lastHit = result;
            }
			return result;
		},
        _getCurrentBounds : function(){
            var bounds = {minColumn: 100000, maxColumn:-100000, minRow:100000, maxRow:-100000};
            iterate(this.cells, function(cell){
                var grid = cell.grid;
                if( bounds.minColumn > grid.col )
                    bounds.minColumn = grid.col;
                if( bounds.maxColumn < grid.col )
                    bounds.maxColumn = grid.col;
                if( bounds.minRow > grid.row )
                    bounds.minRow = grid.row;
                if( bounds.maxRow < grid.row )
                    bounds.maxRow = grid.row;
            });
            return bounds;
        },
        findCellByRoomId : function(roomId){
            return this.findCellByClosure( function(cell){ return cell.roomId == roomId; } )
        },
        findCellByClosure : function( closure ){
            var result = null;
            iterate(this.cells, function(cell){
                if( closure(cell.grid) ) return (result = cell);
            });
            return result;
        },
		translate : function(x, y){
            var self = this;
			var cells = this.cells;
			var meta  = this.gridMeta;

			var totalHeight = (this.total.rows) * meta.height;
			var totalWidth  = (this.total.columns) * meta.width;

            var bounds = this._getCurrentBounds();
			for(var i=0; i < cells.length; i++){
				if( cells[i] ){
					var grid = cells[i].grid;
					var m2d  = grid.node.matrix2d;

                    var loadCell = function(args){
                        var g1 = grid;
						grid.col += args.columns;
                        grid.row += args.rows;
                        var loadOperation = function(){
                            var g2 = g1;
                            console.log("loadOperations on " + g2.roomId);
                            g2.translate(args.width, args.height);
                            self.cellLoader(g2, meta, {row:args.rows, col:args.columns}, args.width, args.height)
                        };
                        if( g1.isDeferred ){
                            g1.next.push(loadOperation);
                        }else{
                            g1.next = [];
                            g1.isDeferred = true;
                            loadOperation();
                        }
                    };

					grid.translate(x, y);

					grid.left = m2d.x3;
					grid.top  = m2d.y3;

                    var loadParam = {
                        width : 0, height : 0, rows : 0, columns : 0
                    };

					if( m2d.x3 > this.container.width && x > 0 && bounds.maxColumn == grid.col ){
                        loadParam.width   = -totalWidth;
                        loadParam.columns = -this.total.columns;
                        loadParam.load = true;
					}else if(m2d.x3 < -meta.width && x < 0 && bounds.minColumn == grid.col ){
                        loadParam.width   = totalWidth;
                        loadParam.columns = this.total.columns;
                        loadParam.load = true;
					}
					if( m2d.y3 > this.container.height && y > 0 && bounds.maxRow == grid.row ){
						loadParam.height = -totalHeight;
                        loadParam.rows   = -this.total.rows;
                        loadParam.load = true;
					}else if( m2d.y3 < -meta.height && y < 0 && bounds.minRow == grid.row){
                        loadParam.height = totalHeight;
                        loadParam.rows   = this.total.rows;
                        loadParam.load = true;
					}
                    if( loadParam.load )
                        loadCell(loadParam);
				}
			}
			m2d = this.m2d;
			m2d.changed = true;
			m2d.translate(x,y);
		}
		,intersects : function(e){}
		,setActive : function(){}
	});
	
});